﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using PServiceBus.Core.Interface;
using PServiceBus.Core.Runtime;
using PServiceBus.Core.Logger;

namespace PServiceBus.Core.Runtime.ComponentServices {
    /// <summary>
    /// Class for processing component services
    /// </summary>
    public static class ComponentServiceProcessor {
        private readonly static Dictionary<Type, int> _intervals = new Dictionary<Type,int>();
        private readonly static Dictionary<Type, bool> _runnings = new Dictionary<Type, bool>();
        private readonly static Dictionary<Type, IComponent> _components = new Dictionary<Type, IComponent>();
        private readonly static Dictionary<Type, ManualResetEvent> _resets = new Dictionary<Type, ManualResetEvent>();
        private readonly static Dictionary<Type, RegisteredWaitHandle> _handles = new Dictionary<Type, RegisteredWaitHandle>();

        /// <summary>
        /// Starts the specified component
        /// </summary>
        /// <typeparam name="TComponent">The type of the component.</typeparam>
        public static void Start<TComponent>() where TComponent : IComponent {
            Start<TComponent>(10);
        }

        /// <summary>
        /// Starts the specified component
        /// </summary>
        /// <typeparam name="TComponent">The type of the component.</typeparam>
        /// <param name="interval">The interval.</param>
        public static void Start<TComponent>(int interval) where TComponent : IComponent {
            StartNew<TComponent>(interval, 10);
        }

        /// <summary>
        /// Starts the specified component
        /// </summary>
        /// <typeparam name="TComponent">The type of the component.</typeparam>
        /// <param name="interval">The interval.</param>
        /// <param name="batchSize">The batch size</param>
        /// <param name="parameters">The parameters that would be passed to the instance of the component.</param>
        public static void StartNew<TComponent>(int interval, int batchSize, params object[] parameters) where TComponent : IComponent {
            var currentType = typeof(TComponent);
            StartNew(currentType, interval, batchSize, parameters);
        }

        public static void StartNew(Type componentType, int interval, int batchSize, params object[] parameters) {
            ThreadPoolBootStrapper.Setup();
            MethodHelper.Try(() =>
                AppDomain.CurrentDomain.UnhandledException += (s, e)
                    => ESBLogger.TraceError((e.ExceptionObject as Exception).ToString()));
            var component = Activator.CreateInstance(componentType, parameters) as IComponent;
            component.BatchSize = batchSize;
            _runnings[componentType] = true;
            _intervals[componentType] = interval;
            if (component != null) component.Init();
            if (!_resets.ContainsKey(componentType))
                _resets[componentType] = new ManualResetEvent(false);
            _resets[componentType].Reset();
            _components[componentType] = component;
            _handles[componentType] = ThreadPool.RegisterWaitForSingleObject(_resets[componentType],
                new WaitOrTimerCallback(ProcessEvent),
                new Tuple<IComponent, Type>(component, componentType),
                interval, true);
        }

        private static void ProcessEvent(object state, bool timeout) {
            var tuple = state as Tuple<IComponent, Type>;
            var component = tuple.Item1;
            var type = tuple.Item2;
            var interval = _intervals[type];
            var reset = _resets[type];
            if (reset == null) return;
            if (timeout) {
                MethodHelper.TryLog(() => component.Execute());
                _handles[type] = ThreadPool.RegisterWaitForSingleObject(reset, new WaitOrTimerCallback(ProcessEvent), state,
                    interval, true);
            } else {
                _handles[type].Unregister(null);
            }
        }

        /// <summary>
        /// Starts the specified component
        /// </summary>
        /// <typeparam name="TComponent">The type of the component.</typeparam>
        /// <param name="interval">The interval.</param>
        /// <param name="batchSize">The batch size</param>
        public static void Start<TComponent>(int interval, int batchSize) where TComponent : IComponent {
            StartNew<TComponent>(interval, batchSize);
        }

        /// <summary>
        /// Shuts down the specified component
        /// </summary>
        /// <typeparam name="TComponent">The type of the component.</typeparam>
        public static void ShutDown<TComponent>() where TComponent : IComponent {
            var type = typeof(TComponent);
            ShutDown(type);
        }

        public static void ShutDown(Type componentType) {
            _runnings[componentType] = false;
            _resets[componentType].Set();
            _components[componentType].Close();
        }

        public static void SaveState<TComponent>() where TComponent : IComponent {
            var type = typeof(TComponent);
            SaveState(type);
        }

        public static void SaveState(Type componentType) {
            _components[componentType].SaveState();
            ShutDown(componentType);
        }
    }
}
